#define __ALIAS_C

/************System include***********************************************/
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>


/************Private include**********************************************/
#include "runtime.h"
#include "alias.h"
#include "config.h"

/************ Defines and Typedefs *****************************************/
/*  #defines and typedefs should have their names in all caps.
 *  Global variables begin with g. Global constants with k. Local
 *  variables should be in all lower case. When initializing
 *  structures and arrays, line everything up in neat columns.
 */

typedef struct alias_storage
{
  bool haveAlias; // if users call an alias command once, this variable is true otherwise false.
  aliasll* all; // a pointer to an alias command
} aliasSto;

/************ Global Variables *********************************************/
aliasSto aliasStorage = {FALSE, NULL};

/************ Function Prototypes ******************************************/
void appendAliasLL(commandT* cmd);
// create an alias linked list from the alias argument and commandline string
// return a pointer to aliasll if successful. Otherwise, return null.
aliasll* createAliasLL(commandT* cmd);
// check whether the command is already aliased or not
// If yes, return the command that the alias command stands for
// If no, return null.
char* findAliasCmd(char* aliasCmd);
// free all dynamically allocated char in the alias linked list
void freeAliasLL(aliasll* allist);
void printAlias();
bool unalias(commandT* cmd);


/************ Function Body ************************************************/
aliasll* createAliasLL(commandT* cmd)
{
   assert(cmd != NULL);
   if(cmd->argc == 2)
   {
      aliasll* allist = malloc(sizeof(aliasll));
      if(strchr(cmd->cmdline, '=') == NULL || strchr(cmd->cmdline, '\'') == NULL 
	 || strrchr(strchr(cmd->cmdline, '\'') + 1, '\'') == NULL)
      { // if the wrong format is enter, print out the error message
	 printf("Alias command with a wrong format input: %s\n", cmd->argv[1]);
	 return NULL;
      }

      char* divided = strchr(cmd->cmdline, '=');
      char* newCom = strchr(cmd->cmdline, ' ') + 1;
      char* lastQuote = strrchr(cmd->cmdline, '\'');
      allist->newCom = (char*)malloc(sizeof(char) * 
				      (strlen(newCom) - strlen(divided) - 1) + 1);
      strncpy(allist->newCom, newCom, (int)(divided - newCom));
      allist->cmdLine = strdup(cmd->cmdline);
      allist->oldCom = (char *)malloc(sizeof(char*) * (size_t)(lastQuote - divided - 2) + 1);
      strncpy(allist->oldCom, divided + 2, (size_t)(lastQuote - divided - 2));
      allist->next = NULL;
      return allist;
   }
   return NULL;
}

// free all dynamically allocated char in the alias linked list
void freeAliasLL(aliasll* allist)
{
  free(allist->newCom);
  free(allist->cmdLine);
  free(allist->oldCom);
  free(allist);
}

// put an alias linked list with others  and sort them
void appendAliasLL(commandT* cmd)
{
   aliasll* allist = createAliasLL(cmd);

   if(allist == NULL)
      return;

   if(!aliasStorage.haveAlias)
   {
      aliasStorage.all = allist;
      aliasStorage.haveAlias = TRUE;
      return;
   } else 
   {
      aliasll* ll = aliasStorage.all;
      aliasll* prev = NULL;
      while((ll != NULL) && (strcmp(ll->newCom, allist->newCom) != 0) && (strcmp(ll->newCom, allist->newCom) < 0))
      {
	 prev = ll;
	 ll = ll->next;
      }

      if(ll == NULL)
      {  // search to the last linked list
	 prev->next = allist;
	 allist->next = NULL;
      } else if(strcmp(ll->newCom, allist->newCom) == 0)
      {  // the same alias command
	 allist->next = prev->next;
	 freeAliasLL(prev->next);
	 prev->next = allist;
      } else if(strcmp(ll->newCom, allist->newCom) > 0)
      {
	 if(prev == NULL)
	 {
	    allist->next = aliasStorage.all;
	    aliasStorage.all = allist;
	 } else 
	 {
	    allist->next = prev->next;
	    prev->next = allist;
	 }
      } else 
	 assert(TRUE);
      return;
   }
}

// unalias the command return true if succeedingm otherwise false.
bool unalias(commandT* cmd)
{
   if(!aliasStorage.haveAlias && (cmd->argv[1] != NULL))
     //printf("unalias: usage: unalias [-a] name [name ...]\n");
     printf("unalias: %s: not found\n", cmd->argv[1]);
   else if ((cmd->argc == 2) && (cmd->argv[1] != NULL))
   {
      aliasll* ll = aliasStorage.all;
      aliasll* prev = NULL;
      int strcmpRes = 0; 
      while((ll != NULL) && ((strcmpRes = strcmp(ll->newCom, cmd->argv[1])) != 0))
      {
	 prev = ll;
	 ll = ll->next;
      }

      if((ll != NULL) && (strcmpRes == 0))
      {
	 if(prev == NULL)
	 {
	    if(ll->next != NULL) // remove ll1
	    {  // all -> ll1 -> ll2
	       aliasStorage.all = ll->next;
	       // all -> ll2
	    } else // remove ll1
	    {  // all -> ll1
	       aliasStorage.haveAlias = FALSE;
	       aliasStorage.all = NULL;
	    } // all -> NULL
	 } else // remove ll2
	 { // all -> ll1 -> ll2 -> ll3
	    prev->next = ll->next;
	    ll->next = NULL;
	 } // all -> ll1 -> ll3
	 freeAliasLL(ll);
	    
	 return TRUE;
      } else if(strcmpRes != 0)
      {  // not found
	 printf("unalias: %s: not found\n", cmd->argv[1]);
	 return FALSE;
      }
   }

   return FALSE;
}

// check whether the command is already aliased or not
// If yes, return the command that the alias command stands for
// If no, return null.
char* findAliasCmd(char* aliasCmd)
{
   if(aliasStorage.haveAlias)
   {
      aliasll* ll = aliasStorage.all;
      while((ll != NULL) && (strcmp(aliasCmd, ll->newCom) != 0))
	 ll = ll->next;
      
      if(ll == NULL)
	 return NULL;
      else 
	 return strdup(ll->oldCom);

   } else 
      return NULL;
}

// print all alias list
void printAlias()
{
   if(!aliasStorage.haveAlias)
   { // if there is nothing in the list
      printf("There is no alias linked list.\n");
      return;
   } else
   {
      aliasll* ll = aliasStorage.all;
      while(ll != NULL)
      {
	 printf("%s\n", ll->cmdLine);
	 ll = ll->next;
      }
   }
}
   



  

